package cloud.coder.remote.boot;

import cloud.coder.remote.grpc.server.GRPCHandler;
import cloud.coder.remote.grpc.server.ServiceServer;
import io.grpc.BindableService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

public enum HandlerManager {
    /**
     * 单例
     */
    INSTANCE;

    private static final Logger LOGGER = LoggerFactory.getLogger(HandlerManager.class);

    public Map<Class, GRPCHandler> getBootedHandlers() {
        return bootedHandlers;
    }

    /**
     * 所有注册的服务
     */
    private Map<Class, GRPCHandler> bootedHandlers = Collections.emptyMap();

    public void registerHandlers(ServiceServer server) {
        bootedHandlers = loadAllHandlers();
        for (Map.Entry<Class, GRPCHandler> entry : bootedHandlers.entrySet()) {
            server.addHandler((BindableService) entry.getValue());
        }
    }

    private Map<Class, GRPCHandler> loadAllHandlers() {
        Map<Class, GRPCHandler> bootedHandlers = new LinkedHashMap<>();
        List<GRPCHandler> allHandlers = new LinkedList<>();
        load(allHandlers);
        for (final GRPCHandler bootService : allHandlers) {
            Class<? extends GRPCHandler> bootHandlerClass = bootService.getClass();
            boolean isDefaultImplementor = bootHandlerClass.isAnnotationPresent(DefaultImplementor.class);
            if (isDefaultImplementor) {
                if (!bootedHandlers.containsKey(bootHandlerClass)) {
                    bootedHandlers.put(bootHandlerClass, bootService);
                } else {
                    //ignore the default service
                }
            } else {
                if (!bootedHandlers.containsKey(bootHandlerClass)) {
                    bootedHandlers.put(bootHandlerClass, bootService);
                } else {
                    throw new ServiceConflictException("Duplicate grpc handler define for :" + bootHandlerClass);
                }
            }
        }
        return bootedHandlers;
    }

    /**
     * Find a {@link GRPCHandler} implementation, which is already instantiated.
     *
     * @param handlerClass class name.
     * @param <T>          {@link GRPCHandler} implementation class.
     * @return {@link GRPCHandler} instance
     */
    public <T extends GRPCHandler> T findService(Class<T> handlerClass) {
        return (T) bootedHandlers.get(handlerClass);
    }

    void load(List<GRPCHandler> allHandlers) {
        for (final GRPCHandler bootService : ServiceLoader.load(GRPCHandler.class, this.getClass().getClassLoader())) {
            allHandlers.add(bootService);
        }
    }
}
